package com.xyzwps.harbor.handler;

import com.xyzwps.harbor.handler.demo.DemoRoutes;
import com.xyzwps.harbor.handler.filter.LogFilter;
import com.xyzwps.harbor.handler.user.UserRoutes;
import com.xyzwps.harbor.util.ErrorCode;
import com.xyzwps.harbor.util.HarborException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.Map;

import static org.springframework.http.MediaType.APPLICATION_JSON;

@Configuration
public class Routes {

    @Bean
    public RouterFunction<ServerResponse> rootRoutes(
            DemoRoutes demo,
            UserRoutes user,
            LogFilter logger
    ) {
        return RouterFunctions.route()
                .filter(logger)
                .path("/api", b -> b
                        .path("/demo", demo::routes)
                        .path("/user", user::routes)
                )
                .onError(Throwable.class, this::errorHandler)
                .build();
    }

    private Mono<ServerResponse> errorHandler(Throwable t, ServerRequest req) {
        log.error("request " + req.methodName() + " " + req.path() + " failed", t);

        var status = HttpStatus.INTERNAL_SERVER_ERROR;
        var body = Map.of("msg", t.getMessage(), "code", ErrorCode.UNHANDLED.code);

        if (t instanceof HarborException) {
            var ex = (HarborException) t;
            status = ex.status;
            body = Map.of("msg", ex.getMessage(), "code", ex.code.code);
        }
        return ServerResponse.status(status).contentType(APPLICATION_JSON).bodyValue(body);
    }


    private static final Logger log = LoggerFactory.getLogger(Routes.class);
}
